from datetime import datetime
from typing import Optional

from pydantic import ValidationInfo, field_validator
from sqlmodel import SQLModel, Field

# 导入枚举
from app.enum import ModuleEnum, ResourceTypeEnum, ActionEnum, ScopeEnum


class Permission(SQLModel, table=True):
    """
    权限表(permissions)
    """
    __tablename__: str = "permission"

    id: int = Field(primary_key=True)
    name: str = Field(max_length=100, unique=True, description="权限名称(中文描述)")
    code: str = Field(max_length=100, unique=True, description="权限唯一编码(用于JWT)")

    resource_type: str = Field(max_length=50, description="资源类型")
    action: str = Field(max_length=30, description="操作类型")
    scope: str = Field(max_length=30, description="作用范围")
    module: str = Field(max_length=50, description="所属模块")

    bit_position: int = Field(unique=True, description="在位图中的位置(用于高性能判断)")
    is_active: bool = Field(default=True, description="是否启用")
    created_at: datetime = Field(default_factory=datetime.now)
    updated_at: datetime = Field(default_factory=datetime.now)

    # ===== 枚举字段校验（Pydantic V2）=====
    @classmethod
    @field_validator('module')
    def validate_module(cls, v: str) -> str:
        if v not in ModuleEnum.__members__.values():
            raise ValueError(f"Invalid module: {v}. Must be one of {list(ModuleEnum)}")
        return v

    @classmethod
    @field_validator('resource_type')
    def validate_resource_type(cls, v: str) -> str:
        if v not in ResourceTypeEnum.__members__.values():
            raise ValueError(f"Invalid resource_type: {v}. Must be one of {list(ResourceTypeEnum)}")
        return v

    @classmethod
    @field_validator('action')
    def validate_action(cls, v: str) -> str:
        if v not in ActionEnum.__members__.values():
            raise ValueError(f"Invalid action: {v}. Must be one of {list(ActionEnum)}")
        return v

    @classmethod
    @field_validator('scope')
    def validate_scope(cls, v: str) -> str:
        if v not in ScopeEnum.__members__.values():
            raise ValueError(f"Invalid scope: {v}. Must be one of {list(ScopeEnum)}")
        return v

    # ===== 自动生成 code 字段（需访问其他字段）=====
    @classmethod
    @field_validator('code', mode='before')
    def generate_code(cls, v: Optional[str], info: ValidationInfo) -> str:
        # 如果用户已提供 code，直接使用
        if v is not None and v != "":
            return v

        # 否则尝试从其他字段生成
        # 注意：在 mode='before' 阶段，其他字段可能还未校验，但值已存在
        data = info.data  # 获取当前已解析的字段字典
        module = data.get('module')
        resource = data.get('resource_type')
        action = data.get('action')
        scope = data.get('scope')

        if all(isinstance(x, str) and x for x in [module, resource, action, scope]):
            return f"{module}:{resource}:{action}:{scope}"
        else:
            # 无法生成，但 code 是必填字段，后续会因为空值报错
            raise ValueError("Cannot generate code")


class Role(SQLModel, table=True):
    """
    角色表(role)
    """
    __tablename__: str = "role"
    id: int = Field(primary_key=True)
    name: str = Field(max_length=50, unique=True)
    code: str = Field(max_length=50, unique=True)
    description: Optional[str] = Field(default=None)
    is_active: bool = Field(default=True)
    is_system: bool = Field(default=False)
    created_at: datetime = Field(default_factory=datetime.now)
    updated_at: datetime = Field(default_factory=datetime.now)


class RolePermission(SQLModel, table=True):
    """
    角色权限关联表(role_permissions)
    """
    __tablename__: str = "role_permission"
    role_id: int = Field(primary_key=True, foreign_key="role.id")
    permission_id: int = Field(primary_key=True, foreign_key="permission.id")
    created_at: datetime = Field(default_factory=datetime.now)


class UserRole(SQLModel, table=True):
    """
    用户角色关联表(user_roles)
    """
    __tablename__: str = "user_role"
    user_id: int = Field(primary_key=True, foreign_key="user.id")
    role_id: int = Field(primary_key=True, foreign_key="role.id")
    granted_by: Optional[int] = Field(default=None, foreign_key="user.id")
    granted_at: datetime = Field(default_factory=datetime.now)
    expired_at: Optional[datetime] = Field(default=None)
    is_active: bool = Field(default=True)
